/**
 * 
 * @param {*} oldVnode 旧虚拟DOM
 * @param {*} vnode 新虚拟DOM
 * @param {*} parentElm 父节点
 * 比较两个节点是否存在或一致
 */
export function patch(oldVnode, vnode, parentELm) {
  if (!oldVnode) {
    parentELm.appendChild(vnode.el)
  } else if (!vnode) {
    parentELm.removeChild(oldVnode.el)
  } else {
    if (isSameNode(oldVnode, vnode)) {
      patchNode(oldVnode, vnode)
    } else {
      parentELm.removeChild(oldVnode.el)
      parentELm.appendChild(vnode.el)
    }
  }
}

/**
 * 
 * @param {*} oldVnode 旧节点
 * @param {*} vnode 新节点
 * 判断是否存在子元素，并且对比两者是否一致
 */
function patchNode(oldVnode, vnode) {
  if (oldVnode == vnode) {
    return;
  }
  // 忽略静态节点之间的对比
  if (oldVnode.isStatic && vnode.isStatic && oldVnode.key === vnode.key) {
    vnode.el = oldVnode.el
    return;
  }
  const el = vnode.el = oldVnode.el
  const newCh = vnode.children
  const oldCh = oldVnode.children
  // 文本节点
  if (vnode.text) {
    el.textContent = vnode.text
  } else {
    if (newCh.length && oldCh.length && newCh != oldCh) {
      updateChildren(el, oldCh, newCh)
    } else if (oldCh.length) {
      removeVnodes(el, oldCh, 0, oldCh.length-1)
    } else if (newCh.length) {
      if (oldVnode.text) el.textContent = ''
      addVnodes(el, newCh, 0, newCh.length-1)
    } else if (oldVnode.text) {
      el.textContent = ''
    }
  }
}

// 添加多个子节点
function addVnodes(parent, children, start, end) {
  for (let i=start; i<=end; i++) {
    parent.appendChild(children[i])
  }
}

// 移除多个子节点
function removeVnodes(parent, children, start, end) {
  for (let i=end; i>=start; i--) {
    parent.removeChild(children[i])
  }
}

/**
 * 
 * @param {*} parent 父元素
 * @param {*} oldCh 旧子节点
 * @param {*} newCh 新子节点
 * 主要比较新旧子节点之间的差异
 * 头头比较、尾尾比较、头尾之间的比较
 * 乱序情况下，生成旧虚拟DOMd的key-index映射，方便新虚拟DOM的开头节点找到置换的位置
 */
function updateChildren(parent, oldCh, newCh) {
  // 旧节点相关的
  let oldStartIdx = 0
  let oldEndIdx = oldCh.length - 1
  let oldStartNode = oldCh[0]
  let oldEndNode = oldCh[oldEndIdx]
  // 新节点相关的
  let newStartIdx = 0
  let newEndIdx = newCh.length - 1
  let newStartNode = newCh[0]
  let newEndNode = newCh[newEndIdx]
  // 生成旧子节点key-index的映射对象
  function makeIdxByKey(children) {
    let map = {}
    children.forEach((item, index) => {
      map[item.key] = index
    })
    return map
  }
  let map = makeIdxByKey(oldCh)
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    if (!oldStartNode) {
      oldStartNode = oldCh[++oldStartIdx]
    } else if (!oldEndNode) {
      oldEndNode = oldCh[--oldEndIdx]
    } else if (isSameNode(oldStartNode, newStartIdx)) {
      // 头头比较
      patchNode(oldStartNode, newStartNode)
      oldStartNode = oldCh[++oldStartIdx]
      newStartNode = newCh[++newStartIdx]
    } else if (isSameNode(oldEndNode, newEndNode)) {
      // 尾尾比较
      patchNode(oldEndNode, newEndNode)
      oldEndNode = oldCh[--oldEndIdx]
      newEndNode = newCh[--newEndIdx]
    } else if (isSameNode(oldStartNode, newEndNode)) {
      // 头尾比较
      patchNode(oldStartNode, newEndNode)
      parent.insertBefore(oldStartNode.el, oldEndNode.el.nextSibling)
      oldStartNode = oldCh[++oldStartIdx]
      newEndNode = newCh[--newEndIdx]
    } else if (isSameNode(oldEndNode, newStartNode)) {
      // 尾头比较
      patchNode(oldEndNode, newStartNode)
      parent.insertBefore(oldEndNode.el, oldStartNode.el)
      oldEndNode = oldCh[--oldEndIdx]
      newStartNode = newCh[++newStartIdx]
    } else {
      let moveIdx = map[newStartNode.key]
      if (!moveIdx) {
        parent.insertBefore(createElm(newStartNode), oldStartNode.el)
        newStartNode = newCh[++newStartIdx]
      } else {
        let moveNode = oldCh[moveIdx - 1]
        patchNode(moveNode, newStartNode)
        oldCh[moveIdx - 1] = undefined
        parent.insertBefore(moveNode.el, oldStartNode.el)
        newStartNode = newCh[++newStartIdx]
      }
    }
  }
  // 只剩下新节点情况
  if (newStartIdx <= newEndIdx) {
    let el = newCh[newEndIdx+1] == null ? null : newCh[newEndIdx+1].el
    for (let i=newStartIdx; i<=newEndIdx; i++) {
      parent.insertBefore(createElm(newCh[i]), el)
    }
  } else if (oldStartIdx <= oldEndIdx) {
    // 只剩下旧节点情况
    for (let i=oldStartIdx; i<=oldEndIdx; i++) {
      if (oldCh[i] != undefined) {
        parent.removeChild(oldCh[i])
      }
    }
  }
}

// 判断是否为相同的节点
function isSameNode(a, b) {
  return a.tag === b.tag && 
  a.key === b.key &&
  a.isComment === b.isComment && 
  (!!a.data) === (!!b.data) &&
  isSameInputType(a, b)
}

// 判断input类型是否一致
function isSameInputType(a, b) {
  if (a.tag != 'input') return true
  let i
  let typeA = (i = i.data) && (i = i.attrs) && (i.type)
  let typeB = (i = i.data) && (i = i.attrs) && (i.type)
  return typeA === typeB
}